#ifndef CURLING_GAME_INCLUDED
#define CURLING_GAME_INCLUDED

#include <vector>
#include <boost/shared_ptr.hpp>
#include "curling/stone.hpp"

namespace curling {

class rink;
class stone;

/**
 * Listener interface for game events.
 */
class game_listener
{
public:
  
  virtual void begin_game();
  virtual void begin_end();
  virtual void begin_player_one_shot();
  virtual void begin_player_two_shot();
  virtual void end_player_one_shot();
  virtual void end_player_two_shot();
  virtual void end_end();
  virtual void end_game_player_one_win();
  virtual void end_game_player_two_win();
};

/**
 * Player type
 * For different AI implementations, list them here.
 */
enum player_type
{
  player_type_human,
  player_type_computer
};

/**
 * Default game
 */
namespace default_game {
  /** Default amount of allocated time to each player in a game, in [s].*/
  const double player_time = 60.0 * 60.0;
  /** Default number of regular ends in a game. */
  const int endings = 8;
  /** Default number of stones for each player in a game. */
  const int stones = 8;
}

/**
 * Interface to the ongoing game.
 * Currently the possible actions are a bit limited, nothing for real curling
 * addictorz.
 */
class game
{
public:
  
  /**
   * Construct a new game.
   * Specify the type of players to use.
   */
  game(
    player_type player_one_type,
    player_type player_two_type,
    int regular_ends = default_game::endings,
    int player_stones = default_game::stones );
  ~game();
  
  /**
   * Get the type of player.
   */
  player_type player_one_type() const;
  player_type player_two_type() const;
  
  /**
   * Get player turn.
   */
  bool player_one_turn() const;
  bool player_two_turn() const;
  
  /**
   * Get playing time left.
   */
  double player_one_time() const;
  double player_two_time() const;
  
  /**
   * Get stones left.
   */
  int player_one_stones_left() const;
  int player_two_stones_left() const;
  
  /*
   * Phases of play
   * 
   * Player Beginning Shot - true at the beginning of a player turn until
   * player_begin_shot() is called.
   *
   * Player Shooting - true from the moment the player starts aiming until no
   * stones are in motion.
   *
   * Player Ending Shot - true after the the stone has stopped moving until
   * player_end_shot() is called.
   */
  bool initial() const;
  bool beginning_game() const;
  bool beginning_end() const;
  bool player_beginning_shot() const;
  bool player_aiming() const;
  bool player_delivering() const;
  bool player_sweeping() const;
  bool player_ending_shot() const;
  bool ending_end() const;
  bool ending_game() const;
  bool final() const;
  
  /*
   * Begin game phase
   */
  void begin_game();
  
  /*
   * Begin end phase
   */
  void begin_end();

  /*
   * Begin shot phase
   */
  void player_begin_shot();
  
  /*
   * Aim
   */
  void player_aim( vector2d target );
  
  /*
   * Deliver
   */
  void player_deliver( double direction, double power, double spin );
  
  /*
   * Sweep
   */
  void player_sweep();
  
  /*
   * End shot phase
   */
  void player_end_shot();
  
  /*
   * End end phase
   */
  void end_end();
  
  /*
   * End game phase
   */
  void end_game();
  
  /*
   * The rink being used for play.
   */
  boost::shared_ptr<rink> rink_played();
  
  /*
   * The stone currently being played.
   */
  boost::shared_ptr<stone> stone_played();
  
  /*
   * Simulation
   */
  void simulate( double dt );

  /**
   * Get number of endings in game, not counting "sudden death".
   */
  int regular_endings() const;
  /**
   * Get current ending being played.
   */
  int current_ending() const;
  
  int player_one_end_score( int end ) const;
  int player_two_end_score( int end ) const;
  
  int player_one_score() const;
  int player_two_score() const;
  
protected:
  
  void start_player_one_clock();
  void stop_player_one_clock();
  void start_player_two_clock();
  void stop_player_two_clock();

private:

  boost::shared_ptr<rink> _rink_played;
  boost::shared_ptr<stone> _stone_played;
  
  player_type _player_one_type;
  player_type _player_two_type;
  double _player_one_time;
  double _player_two_time;
  
  enum state
  {
    state_initial = 0,
    state_beginning_game,
    state_beginning_end,
    state_player_beginning_shot,
    state_player_aiming,
    state_player_delivering,
    state_player_sweeping,
    state_player_ending_shot,
    state_ending_end,
    state_ending_game,
    state_final
  };
  
  state _current_state;
  
  enum turn
  {
    turn_none,
    turn_player_one,
    turn_player_two
  };
  
  turn _current_turn;
  //int _player_turn;
  bool _player_one_clock_ticking;
  bool _player_two_clock_ticking;
  
  state current_state() const;
  void switch_state( state next_state );
  
  double _player_direction;
  double _player_power;
  double _player_spin;
  
  /*
   * Find a stone of the current color which has not been played. Make it the
   * stone played and put it into the correct starting position.
   */
  void find_and_setup_stone( stone_color color );
  
  int _regular_endings;
  int _current_ending;
  std::vector<int> _player_one_ends;
  std::vector<int> _player_two_ends;
  
  /**
   * Tag stones as points.
   */
  void update_point_stones();
  /**
   * Record current end into _player_one_ends and _player_two_ends.
   */
  void record_score();
};

}

#endif
